<?php
declare (strict_types = 1);

namespace app\admin\middleware;

use app\admin\annotation\Permission;
use app\admin\cache\PermissionCache;
use app\admin\cache\RoleAuthCache;
use app\admin\logic\auth\Token;
use app\admin\service\InteractsWithPermission;
use app\common\validate\BaseValidate;
use app\lib\exception\DevException;
use app\lib\exception\ParameterException;
use app\lib\exception\RequestMethodException;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\Reader;
use think\facade\Log;
use think\Response;

class CheckAuth
{
    use InteractsWithPermission;

    /** @var Reader */
    protected $reader;

    /**
     * 校验权限问题
     *
     * @param \think\Request $request
     * @param \Closure $next
     * @return Response
     * @throws RequestMethodException
     * @throws DevException
     * @throws \app\lib\exception\TokenException
     * @throws ParameterException
     * @throws \app\lib\exception\AuthException
     * @throws \Doctrine\Common\Annotations\AnnotationException
     * @throws \ReflectionException
     */
    public function handle($request, \Closure $next)
    {
        $controller = $request->controller();
        $action = $request->action();
        $className = app()->getNamespace(). "\\controller\\" . $controller;
        $key = $className . "::" . $action;

        if (empty($action)) {
            // 未找到方法，直接从路由中抽取
            $route = $request->rule()->getRoute();
            if (empty($route)) {
                throw new DevException('请给接口注册@Route路由');
            }
            [$className, $action] = explode('@', $route);
            $key = str_replace("@", "::", $route);
        }
        /** @var Permission $permission */
        $permission = PermissionCache::instance()->getOne($key);
        if (empty($permission)) {
            //权限不存在，反射获取
            $this->reader = new AnnotationReader();
            $permission = $this->getClassAnnotations($className, $action);
        }
        if ($permission === null) {
            //反射获取也没获取到，通知开发者错误
            throw new DevException('访问的方法未添加@Permission注解');
        }
        if ($permission->method !== "*" && $permission->method !== $request->method()) {
            //请求方法不正确
            throw new RequestMethodException('请求方式需要为:' . $permission->method);
        }
        //继续校验登录等权限
        if ($permission->type !== 'OPEN') {
            //校验登录
            $token = Token::instance()->checkToken();
            $authCache = RoleAuthCache::instance($token->getRoleID());
            if ($permission->type === 'AUTH') {
                //接口需要分配权限，校验权限
                $authCache->check($permission->key);
            }
        }

        if ($permission->validate !== null) {
            // 接口添加了验证器，进行校验
            if (!class_exists($permission->validate->value)) {
                throw new DevException('@Validate注解错误');
            }
            /** @var BaseValidate $validate */
            $validate = new $permission->validate->value;
            $check = $validate->scene($permission->validate->scene)
                ->check($request->param());
            if (!$check) {
                throw new ParameterException($validate->getError());
            }
        }

        $response = $next($request);
        return $response;
    }
}
